/* gears.c
 *
 * Copyright 2007-2008 OpenMoko, Inc.
 * Authored by Tick <tick@openmoko.org>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#include <stdlib.h>
#include <gears.h>
#include <assassin.h>
#include <debug.h>
#include <browser.h>

#define _GNU_SOURCE
#include <string.h>

extern Ecore_Evas  *   ee;
static Ecore_Animator *__EA__ = NULL;

int WIDTH()  {
    int __width;
    evas_output_size_get(ecore_evas_get(ee), &__width, NULL);
    return __width;
}
int HEIGHT()  {
    int __height;
    evas_output_size_get(ecore_evas_get(ee), NULL, &__height);
    return __height;
}

void dummy_mask_cb (void *data,  Evas_Object *obj, const char *emission, const char *source) {
    debug("There is a invisible shield! you cannot touch me!! LALALA!!\n");
    debug("╰(°▽ ° )╯ lalala ╰( ° ▽°)╯ \n");
}
static Evas_Object *__the_mask_cover_on_screen__ = NULL;
static Evas_Object *__the_dim_cover_on_screen__ = NULL;
void put_a_mask_on_screen() {
    debug("Put the mask on\n");
    if (!__the_mask_cover_on_screen__) {
        __the_mask_cover_on_screen__ =  assassin_edje_object_get("assassin/action/mask");
        edje_object_signal_callback_add(__the_mask_cover_on_screen__,
                                        "mouse,clicked,1",
                                        "base",
                                        &dummy_mask_cb,
                                        NULL);
    }
    evas_object_resize(__the_mask_cover_on_screen__, WIDTH(), HEIGHT());
    evas_object_move(__the_mask_cover_on_screen__, 0 , 0);
    evas_object_show(__the_mask_cover_on_screen__);
    evas_object_raise(__the_mask_cover_on_screen__);
}
void remove_the_mask_on_screen() {
    debug("Take the mask off\n");
    if (!__the_mask_cover_on_screen__) {
        error("The Mask should be there\n");
        return;
    } else if (!evas_object_visible_get(__the_mask_cover_on_screen__)) {
        debug("The mask is already took off.\n");
    }
    evas_object_hide(__the_mask_cover_on_screen__);
}

void put_a_dim_on_screen() {
    debug("Put the dim on\n");
    if (!__the_dim_cover_on_screen__) {
        __the_dim_cover_on_screen__ =  assassin_edje_object_get("assassin/busy/dim");
        evas_object_resize(__the_dim_cover_on_screen__, WIDTH(), HEIGHT());
        evas_object_move(__the_dim_cover_on_screen__, 0 , 0);
        edje_object_signal_callback_add(__the_dim_cover_on_screen__,
                                        "mouse,clicked,1",
                                        "base",
                                        &dummy_mask_cb,
                                        NULL);
    } else if (evas_object_visible_get(__the_dim_cover_on_screen__)) {
        debug("The Dim is already there. You may need to remove it\n");
    }
    evas_object_show(__the_dim_cover_on_screen__);
    evas_object_raise(__the_dim_cover_on_screen__);
}
void remove_the_dim_on_screen() {
    debug("Take the dim off\n");
    if (!__the_dim_cover_on_screen__) {
        error("The Dim should be there\n");
        return;
    }
    evas_object_hide(__the_dim_cover_on_screen__);
}


struct Installer_Alert * __new_Alert(Evas *evas, const char *type, const char *message, const char *icon_p, const char *icon_n, CallbackFunc pos, CallbackFunc neg, void *data) {
    struct Installer_Alert * AA = (struct Installer_Alert *)malloc(sizeof(struct Installer_Alert));
    const int alert_height = 185;
    int x, y, w, h;
    put_a_mask_on_screen();
    AA->alert = assassin_edje_object_get("assassin/confirm.alert");
    edje_object_signal_emit(AA->alert, type, "title");
    edje_object_part_text_set(AA->alert, "title", type);
    edje_object_part_text_set(AA->alert, "message", message);
    edje_object_part_text_set(AA->alert, "button.positive", icon_p);
    edje_object_part_text_set(AA->alert, "button.negative", icon_n);
    evas_output_viewport_get(evas, &x, &y, &w, &h);
    evas_object_resize(AA->alert, w, alert_height);
    animator_move_object (AA->alert, FINISH_TIME_MAGIC, x, -alert_height, x, (h - alert_height) / 2);

    // Set Callbacks
    alert_set_callback (AA, pos, neg);

    AA->data = data;
    evas_object_move(AA->alert, -1000, -1000);
    evas_object_show(AA->alert);
    return AA;
}

void dummy_positive_cb (void *data,  Evas_Object *obj, const char *emission, const char *source) {
    debug("Positive!!\n");
}

void dummy_negative_cb (void *data,  Evas_Object *obj, const char *emission, const char *source) {
    debug("Negative!!\n");
}

int alert_set_callback(struct Installer_Alert *AA, CallbackFunc pos, CallbackFunc neg) {
    if (!AA) return 0;
    if (AA->positive && pos != AA->positive) {
        edje_object_signal_callback_del(AA->alert, "mouse,clicked,1", "icon.positive", AA->positive);
        debug("remove old callback!!\n");
    }
    edje_object_signal_callback_add(AA->alert, "mouse,clicked,1", "icon.positive", pos, AA);
    debug("adding new callback!\n");
    AA->positive = pos;
    if (AA->negative && neg != AA->negative) {
        edje_object_signal_callback_del(AA->alert, "mouse,clicked,1", "icon.negative", AA->negative);
    }
    edje_object_signal_callback_add(AA->alert, "mouse,clicked,1", "icon.negative", neg, AA);
    AA->negative = neg;
    return 1;
}

struct Installer_Alert * new_Alert (Evas *evas, const char *type, const char *message, const char *icon_p, const char *icon_n, void *data) {
    CallbackFunc  pos = &dummy_positive_cb;
    CallbackFunc  neg = &dummy_negative_cb;
    return __new_Alert(evas, type, message, icon_p, icon_n, pos, neg, data);
}



void free_Alert(struct Installer_Alert *AA) {
    remove_the_mask_on_screen();
    evas_object_del(AA->alert);
    free(AA);
}

struct Installer_Progressbar * new_Progressbar(Evas *evas, const char *text) {
    int w, h;
    struct Installer_Progressbar * IP = (struct Installer_Progressbar *) malloc (sizeof(struct Installer_Progressbar));
    memset(IP, 0, sizeof(struct Installer_Progressbar));
    debug("text = %s\n", text);
    IP->bar = assassin_edje_object_get("assassin/action.progressbar");
    progressbar_text_set(IP, text);
    IP->MAX = 100;
    IP->WIDTH = WIDTH();
    IP->progress = 0;
    progressbar_update(IP, IP->progress);
    edje_object_size_max_get(IP->bar, &w, &h);
    evas_object_resize(IP->bar, w, h);
    evas_object_move(IP->bar, 0, (HEIGHT() - h) / 2);
    evas_object_show(IP->bar);
    put_a_mask_on_screen();
    return IP;
}

unsigned int progressbar_increase(struct Installer_Progressbar *AP, int v) {
    AP->progress += v;
    return progressbar_update(AP, AP->progress);
}

static inline void __progressbar_update(struct Installer_Progressbar *AP, unsigned int p) {
    double rate;
    Evas_Object * _bar;
    int progress_height;
    int y;
    _bar = (Evas_Object *)edje_object_part_object_get(AP->bar, "progressbar");
    if (_bar == NULL) {
        error("Hey progressbar object is gone!!\n");
    }
    evas_object_geometry_get(_bar, NULL, &y, NULL, &progress_height);
    rate = (double) p / AP->MAX;
    evas_object_move(_bar, 0, y);
    evas_object_resize(_bar, AP->WIDTH*rate, progress_height);
    evas_object_show(_bar);
    return;
}

unsigned int progressbar_text_set(struct Installer_Progressbar *AP, const char *text) {
    if (AP->text) {
        free(AP->text);
    }
    if (text == NULL) {
        debug("Text is NULL!!\n");
        return 0;
    }
    AP->text = malloc(32);
    AP->text[0] = '\0';
    strncpy(AP->text, text, 32);
    edje_object_part_text_set(AP->bar, "message", AP->text);
    __progressbar_update(AP, AP->progress);
    return 1;
}

unsigned int progressbar_update(struct Installer_Progressbar *AP, unsigned int p) {
    char buf[64];  // Warning there may be buffer overflow issues.
    AP->progress = p;
    AP->progress = AP->progress <= AP->MAX ?  AP->progress : AP->MAX;
    AP->progress = AP->progress >= 0 ?  AP->progress : 0;

    snprintf(buf, sizeof(buf), "%s: %3d%%", AP->text, AP->progress);
    edje_object_part_text_set(AP->bar, "message", buf);

    __progressbar_update(AP, p);

    return AP->progress;
}


static Ecore_Animator * __progressbar_animator__ = NULL;
static double __progressbar_animator_start_time;
static int _progressbar_animator_function_(void *data) {
    struct Installer_Progressbar *AP = (struct Installer_Progressbar *) data;
    Evas_Object *bar = (Evas_Object *)edje_object_part_object_get(AP->bar, "progressbar");
    int width, y;
    double elapse = ecore_time_get() - __progressbar_animator_start_time;
    double R;
    const double speed_magic = 3;
    if (__progressbar_animator__ == NULL)
        return 0;
    evas_object_geometry_get(bar, NULL, &y, &width, NULL);
    R = (WIDTH() - width) / 2;
    evas_object_move(bar, R - R * cos(elapse * speed_magic), y);
    put_a_mask_on_screen();
    return 1;
}

void progressbar_animator_start(struct Installer_Progressbar *AP) {
    Evas_Object *bar;
    int height;
    double width;
    const double animator_ratio_magic = 0.2f;
    if (__progressbar_animator__) {
        debug("Already running\n");
        return;
    }
    bar = (Evas_Object *)edje_object_part_object_get(AP->bar, "progressbar");
    evas_object_geometry_get(bar, NULL, NULL, NULL, &height);
    width = animator_ratio_magic * WIDTH();
    evas_object_resize(bar, width, height);
    put_a_mask_on_screen();
    __progressbar_animator_start_time = ecore_time_get();
    __progressbar_animator__ = ecore_animator_add(&_progressbar_animator_function_, AP);
}

void progressbar_animator_stop(struct Installer_Progressbar *AP) {
    if (__progressbar_animator__) {
        ecore_animator_del(__progressbar_animator__);
        __progressbar_animator__ = NULL;
        put_a_mask_on_screen();
    }
}

void free_Progressbar(struct Installer_Progressbar *AP) {
    progressbar_animator_stop(AP);
    evas_object_del(AP->bar);
    free(AP->text);
    free(AP);
    remove_the_mask_on_screen();
}

struct Buttom_Bottons * BB = NULL;

// FIXME  not themeable
static int __back_arrow_color_changing_back(void *data) {
    Evas_Object *arrow = (Evas_Object *)data;
    evas_object_color_set(arrow, 255, 255, 255, 255);
    return 0;
}

static void back_arrow_color_change_green() {
    Evas_Object *arrow;
    if (!BB) {
        error("BB is not there!!\n");
        return ;
    }
    arrow = (Evas_Object *)edje_object_part_object_get(BB->buttons, "backarrow.image");
    if (!arrow) return;
    evas_object_color_set(arrow, 0, 255, 0, 255);
    ecore_timer_add(FINISH_TIME_MAGIC, &__back_arrow_color_changing_back, (void *)arrow);
}


static void BB_install_selected_cb(void *data, Evas_Object *obj, const char *emission, const char *source) {
    struct Buttom_Bottons * BB = (struct Buttom_Bottons *) data;
    BB->selected = 0;
    scroll_page_back_to_root();
    edje_object_signal_emit(obj, "install.button.pressed", "install.button");
}
static void BB_update_selected_cb(void *data, Evas_Object *obj, const char *emission, const char *source) {
    struct Buttom_Bottons * BB = (struct Buttom_Bottons *) data;
    BB->selected = 1;
    scroll_page_back_to_root();
    edje_object_signal_emit(obj, "update.button.pressed", "update.button");
}
static void BB_uninstall_selected_cb(void *data, Evas_Object *obj, const char *emission, const char *source) {
    struct Buttom_Bottons * BB = (struct Buttom_Bottons *) data;
    BB->selected = 2;
    scroll_page_back_to_root();
    edje_object_signal_emit(obj, "uninstall.button.pressed", "uninstall.button");
}
static void BB_backarrow_selected_cb(void *data, Evas_Object *obj, const char *emission, const char *source) {
    scroll_page_back();
    back_arrow_color_change_green();
}




void auto_adjust_button_bar(struct Buttom_Bottons *BB) {
    evas_object_resize(BB->buttons, WIDTH(), BAR_HEIGHT);
    evas_object_move(BB->buttons, 0, HEIGHT() - BAR_HEIGHT);
    evas_object_show(BB->buttons);
}

struct Buttom_Bottons *  new_button_bar(Evas *evas) {
    if (!BB) {
        BB = malloc(sizeof(struct Buttom_Bottons));
    } else {
        return BB;
    }
    BB->buttons =  assassin_edje_object_get("assassin/buttons");
    auto_adjust_button_bar(BB);
    BB->selected = 0;
    edje_object_signal_callback_add(BB->buttons, "mouse,clicked,1", "install.button", &BB_install_selected_cb, BB);
    edje_object_signal_callback_add(BB->buttons, "mouse,clicked,1", "update.button", &BB_update_selected_cb, BB);
    edje_object_signal_callback_add(BB->buttons, "mouse,clicked,1", "uninstall.button", &BB_uninstall_selected_cb, BB);
    edje_object_signal_callback_add(BB->buttons, "mouse,clicked,1", "asterisk.button", &BB_backarrow_selected_cb, BB);
    put_a_dim_on_screen();
    return BB;
}

int get_button_bar_selected_type() {
    if (!BB) {
        error("BB is null!!\n");
        return 0;
    }
    return BB->selected;
}


void button_bar_back_arrow_show(int TF) {
    Evas_Object *arrow;
    Evas_Object *asterisk;
    if (!BB) {
        error("BB is not there!!\n");
        return ;
    }
    arrow = (Evas_Object *)edje_object_part_object_get(BB->buttons, "backarrow.image");
    asterisk = (Evas_Object *)edje_object_part_object_get(BB->buttons, "asterisk.text");
    if (!arrow) {
        error("arrow is not there!!\n");
        return;
    }
    if (!!TF) {
        evas_object_show(arrow);
        evas_object_hide(asterisk);
        back_arrow_color_change_green();
    } else {
        evas_object_hide(arrow);
        evas_object_show(asterisk);
    }
}

#include <packagekit/pk-package-id.h>
Evas_Object * new_package_view (Evas * evas, char *packageid) {
    PkPackageId *pkid = NULL;
    Evas_Object *obj = NULL;

    if (!pk_package_id_check(packageid)) {
        error("Bad Packageid '%s' WARNING THE RESULT MAY INCORRECT!!\n", packageid);
    } else {
        pkid     = pk_package_id_new_from_string(packageid);
    }
    obj = assassin_edje_object_get("assassin/package.detail");
    if (!obj) {
        error("Create obj failed!!\n");
        if (pkid)
            pk_package_id_free(pkid);
        return NULL;
    }
    if (pkid) {
        edje_object_part_text_set(obj, "package.version.text", (char *)pkid->version);
        pk_package_id_free(pkid);
    }
    return obj;
}



static int __animator_square_move_object(void *object_shift) {
    struct Object_Shift  *OS = (struct Object_Shift  *) object_shift;
    double elapse = ecore_time_get() - OS->start_time;
    double ratio;
    double x, y;
    if (elapse >= OS->finish_time) {
        evas_object_move(OS->obj, OS->e_x, OS->e_y);
        free(OS);
        return 0;
    }
    ratio = 1 - (elapse / OS->finish_time);
    ratio = 1 - (ratio * ratio * ratio);
    x = OS->s_x + ratio * (OS->e_x - OS->s_x);
    y = OS->s_y + ratio * (OS->e_y - OS->s_y);
    evas_object_move(OS->obj, x, y);
    return 1;
}

void animator_move_object(Evas_Object *o, double time, int sx, int sy, int ex, int ey) {
    struct Object_Shift  *OS;
    if (o == NULL) {
        debug("o is NULL!!\n");
        return;
    }
    if (time <= 0) {
        evas_object_move(o, ex, ey);
        return;
    }
    OS = (struct Object_Shift *) malloc (sizeof(struct Object_Shift));
    OS->obj = o;
    OS->finish_time = time;
    OS->s_x = sx;
    OS->s_y = sy;
    OS->e_x = ex;
    OS->e_y = ey;
    OS->start_time = ecore_time_get();
    ecore_animator_add(__animator_square_move_object, OS);
}



Evas_Object * assassin_edje_object_get(const char *name) {
    return _assassin_edje_object_get(ecore_evas_get(ee), name);
}

Evas_Object * _assassin_edje_object_get(Evas *evas, const char *name) {
    char buf[64];
    Evas_Object *obj;
    snprintf(buf, sizeof(buf), "%s/assassin.edj", THEME_PATH);
    if (!evas) {
        error("Evas is NULL!!\n");
        return NULL;
    }
    if (!name) {
        error("Name is NULL, what object you want to get??\n");
        return NULL;
    }
    obj = edje_object_add(evas);
    edje_object_file_set(obj, buf, name);
    return obj;
}

struct Scroll_Page * scroll_page_new () {

    struct Scroll_Page * SP = (struct Scroll_Page *) malloc(sizeof(struct Scroll_Page));
    char buf[64];
    memset(SP, 0, sizeof(struct Scroll_Page));

    snprintf(buf, sizeof(buf), "%s/assassin.edj", THEME_PATH);

    SP->frame = edje_object_add(ecore_evas_get(ee));
    evas_object_resize(SP->frame, WIDTH(), HEIGHT());
    edje_object_file_set(SP->frame, buf, "assassin/v.scroll");
    SP->next = SP->prev = SP;
    return SP;
}

void scroll_page_set_volatilize(struct Scroll_Page *sp, int v) {
    sp->volatilize = 1;
}
void scroll_page_set_title(struct Scroll_Page *sp, char *title) {
    if (!sp) {
        error("SP is NULL!!\n");
        return;
    }
    if (!title) {
        title = "";
    }
    edje_object_part_text_set(sp->frame, "title", title);
}


void scroll_page_del (struct Scroll_Page *sp) {
    if (!sp) return;
    if (sp->embed && sp->free_embed)  {
        sp->free_embed(sp->embed);
        sp->embed = NULL;
    }
    evas_object_del(sp->frame);
    sp->frame = NULL;
    free(sp);
}


enum Move_Direction {
    MOVE_TO_LEFT = 0,
    MOVE_TO_RIGHT,
    MOVE_TO_UP,
    MOVE_TO_DOWN,
};
struct GroupMove {
    int direction;
    struct Scroll_Page *old_obj;
    struct Scroll_Page *new_obj;
    Evas_Object *moving_dragger;
    double finish_time;
    double start_time;
};

void __page_size_adjustion(struct Scroll_Page *SP) ;
static int __scroll_move_animator(void *data) {
    struct GroupMove *gm = (struct GroupMove *)data;
    double elapse = ecore_time_get() - gm->start_time;
    double ratio;
    double offset;
    if (elapse >= gm->finish_time) {
        evas_object_hide(gm->old_obj->frame);
        evas_object_del(gm->moving_dragger);
        current_scroll_page_set(gm->new_obj);
        if (gm->old_obj->volatilize) {
            gm->new_obj->next = gm->new_obj->next == gm->old_obj ? NULL : gm->new_obj->next;
            gm->new_obj->prev = gm->new_obj->prev == gm->old_obj ? NULL : gm->new_obj->prev;
            scroll_page_del(gm->old_obj);
        }
        free(gm);
        __EA__ = NULL;
        remove_the_mask_on_screen();
        return 0;
    }
    ratio = 1 - (elapse / gm->finish_time);
    ratio = 1 - (ratio * ratio  * ratio * ratio );
    if (gm->direction == MOVE_TO_LEFT) {
        offset = WIDTH() * ratio;
        evas_object_move(gm->moving_dragger, (int)0 - offset, 0);
    } else if (gm->direction == MOVE_TO_RIGHT) {
        offset =  WIDTH() * ratio;
        evas_object_move(gm->moving_dragger, offset - WIDTH(), 0);
    } else if (gm->direction == MOVE_TO_UP) {
        offset = HEIGHT() * ratio;
        evas_object_move(gm->moving_dragger, 0, -offset);
    } else {  // DOWN
        offset = HEIGHT() * ratio;
        evas_object_move(gm->moving_dragger, 0, offset - HEIGHT());
    }
    return 1;
}

static void scroll_move_animator(struct GroupMove *GM) {
    Evas_Object *moving_panel;
    if (!GM) {
        error("GM is NULL!!\n");
    }
    evas_object_resize(GM->new_obj->frame, WIDTH(), HEIGHT());
    evas_object_resize(GM->old_obj->frame, WIDTH(), HEIGHT());

    if (GM->direction == MOVE_TO_RIGHT || GM->direction == MOVE_TO_LEFT) {
        moving_panel = assassin_edje_object_get("hscroll.page.swallow");
        evas_object_resize(moving_panel, WIDTH()*2, HEIGHT());
        if (GM->direction == MOVE_TO_RIGHT) {
            evas_object_move(moving_panel, -WIDTH(), 0);
            edje_object_part_swallow(moving_panel, "left.swallow", GM->new_obj->frame);
            edje_object_part_swallow(moving_panel, "right.swallow", GM->old_obj->frame);
        } else {
            evas_object_move(moving_panel, 0, 0);
            edje_object_part_swallow(moving_panel, "right.swallow", GM->new_obj->frame);
            edje_object_part_swallow(moving_panel, "left.swallow", GM->old_obj->frame);

        }
    } else {
        moving_panel = assassin_edje_object_get("vscroll.page.swallow");
        evas_object_resize(moving_panel, WIDTH(), HEIGHT()*2);
        if (GM->direction == MOVE_TO_UP) {
            evas_object_move(moving_panel, 0, 0);
            edje_object_part_swallow(moving_panel, "upper.swallow", GM->old_obj->frame);
            edje_object_part_swallow(moving_panel, "lower.swallow", GM->new_obj->frame);
        } else {
            evas_object_move(moving_panel, 0, -HEIGHT());
            edje_object_part_swallow(moving_panel, "upper.swallow", GM->new_obj->frame);
            edje_object_part_swallow(moving_panel, "lower.swallow", GM->old_obj->frame);
        }
    }
    GM->moving_dragger = moving_panel;
    evas_object_show(moving_panel);
    evas_object_raise(new_button_bar(ecore_evas_get(ee))->buttons);
    put_a_mask_on_screen();
    __EA__ = ecore_animator_add(__scroll_move_animator, GM);
}

void scroll_page_back_to_root() {
    struct GroupMove *gm;
    struct Scroll_Page *sp = current_scroll_page_get();
    if (!sp)
        return;
    if (!get_categories_page()) 
        return;
    if (__EA__) {
        debug("_EA_ is runinng, skip this request!!\n");
        return;
    }
    if (get_button_bar_selected_type() == UPDATE && get_categories_page() == sp) {
        return;
    } else if (get_categories_page() == sp) {
        get_categories_page();
        error("Hun!!\n");
        return;
    }
    gm = (struct GroupMove *) malloc(sizeof(struct GroupMove));
    memset(gm, 0, sizeof(struct GroupMove));

    gm->direction = MOVE_TO_RIGHT;
    gm->old_obj = sp;
    gm->new_obj = get_categories_page() ;
    __page_size_adjustion(gm->new_obj);
    gm->finish_time = FINISH_TIME_MAGIC;
    gm->start_time =  ecore_time_get();
    scroll_move_animator(gm);
    button_bar_back_arrow_show(0);
}

void scroll_page_back() {
    struct GroupMove *gm;
    struct Scroll_Page *sp = current_scroll_page_get();
    if (!sp)
        return;
    if (!get_categories_page()) 
        return;

    if (__EA__) {
        debug("_EA_ is runinng, skip this request!!\n");
        return;
    }

    if ( sp->prev == NULL || sp->prev == sp) {
        error("Hun!!\n");
        return;
    }
    gm = (struct GroupMove *) malloc(sizeof(struct GroupMove));
    memset(gm, 0, sizeof(struct GroupMove));

    gm->direction = MOVE_TO_RIGHT;
    gm->old_obj = sp;
    gm->new_obj = sp->prev;
    __page_size_adjustion(gm->new_obj);
    gm->finish_time = FINISH_TIME_MAGIC;
    gm->start_time =  ecore_time_get();
    scroll_move_animator(gm);
    if (get_categories_page() == sp->prev) {
        button_bar_back_arrow_show(0);
    }
}

void scroll_page_shows(struct Scroll_Page *sp) {
    struct GroupMove *gm;
    if (__EA__) {
        debug("_EA_ is runinng, skip this request!!\n");
        return;
    }
    if (!sp) {
        error("Hey!! Don't play me!! sp is NULL\n");
        return;
    }
    if (sp == current_scroll_page_get()) {
        current_scroll_page_set(sp);
        return;
    }
    gm = (struct GroupMove *) malloc(sizeof(struct GroupMove));
    memset(gm, 0, sizeof(struct GroupMove));

    gm->direction = MOVE_TO_LEFT;
    gm->old_obj = current_scroll_page_get();
    gm->new_obj = sp;

    gm->old_obj->next = gm->new_obj;
    gm->new_obj->prev = gm->old_obj;
    __page_size_adjustion(gm->new_obj);

    gm->finish_time = FINISH_TIME_MAGIC;
    gm->start_time =  ecore_time_get();
    scroll_move_animator(gm);
}

struct Scroll_Page * __current_scroll_page__ = NULL;
struct Scroll_Page *current_scroll_page_get() {
    return __current_scroll_page__;
}
void current_scroll_page_set(struct Scroll_Page *SP) {
    struct Buttom_Bottons * B = new_button_bar(ecore_evas_get(ee));
    __current_scroll_page__ = SP;
    evas_object_move(SP->frame, 0, 0);
    __page_size_adjustion(SP);
    evas_object_raise(B->buttons);  // make the buttons on the top
}
void __page_size_adjustion(struct Scroll_Page *SP) {
    int x, y, w, h;
    if (!SP) {
        put_a_mask_on_screen();
        return;
    }
    evas_output_viewport_get(ecore_evas_get(ee), &x, &y, &w, &h);
    evas_object_resize(SP->frame, w, h);
    evas_object_show(SP->frame);
}

Etk_Tree_Row *  scroll_list_append(struct Scroll_List * SL, const char * title) {
    int stringlen = strlen(title) + 64;
    char *buf = (char *)malloc(stringlen);
    snprintf(buf, stringlen, "<b><font_size=48>%s</></b>", title);
    Etk_Tree_Row *tree_row;
    tree_row = etk_tree_row_append(ETK_TREE(SL->tree), NULL, SL->col, buf, NULL );
    etk_tree_thaw(ETK_TREE(SL->tree));
    free(buf);
    return tree_row;
}

void scroll_list_clear(struct Scroll_List *SL) {
    etk_tree_clear(ETK_TREE(SL->tree));
    etk_tree_thaw(ETK_TREE(SL->tree));
}

struct Scroll_List * scroll_list_new(struct Scroll_Page * SP) {
    struct Scroll_List * SL = (struct Scroll_List *)malloc (sizeof(struct Scroll_List));
    memset(SL, 0, sizeof(struct Scroll_List));
    int h, w;

    SL->embed  = etk_embed_new(ecore_evas_get(ee));
    SL->tree  = etk_tree_new();
    etk_scrolled_view_dragable_set(ETK_SCROLLED_VIEW(etk_tree_scrolled_view_get(ETK_TREE(SL->tree))), ETK_TRUE);
    etk_scrolled_view_policy_set(etk_tree_scrolled_view_get(ETK_TREE(SL->tree)), ETK_POLICY_HIDE, ETK_POLICY_HIDE);
    etk_scrolled_view_drag_bouncy_set(ETK_SCROLLED_VIEW(etk_tree_scrolled_view_get(ETK_TREE(SL->tree))), ETK_BOUNCY_STOPTOOBJECT);
    etk_scrolled_view_extra_vmargin_set(ETK_SCROLLED_VIEW(etk_tree_scrolled_view_get(ETK_TREE(SL->tree))), HEIGHT()/2);

    etk_tree_mode_set(ETK_TREE(SL->tree), ETK_TREE_MODE_LIST);
    etk_tree_multiple_select_set(ETK_TREE(SL->tree), ETK_FALSE);
    etk_tree_rows_height_set (ETK_TREE(SL->tree), 60);

    edje_object_part_geometry_get(SP->frame, "scroll.frame", NULL, NULL, &w, &h);
    SL->col = etk_tree_col_new(ETK_TREE(SL->tree), NULL, w, 0.0);
    etk_tree_col_model_add(SL->col, etk_tree_model_text_new());
    etk_tree_headers_visible_set(ETK_TREE(SL->tree), 0);
    etk_tree_build(ETK_TREE(SL->tree));

    etk_container_add(ETK_CONTAINER(SL->embed), SL->tree);

    if (SP) {
        edje_object_part_swallow(SP->frame, "scroll.frame", etk_embed_object_get(ETK_EMBED(SL->embed)));
    }
    etk_widget_show_all(SL->embed);
    return SL;
}


Evas_Object * get_thumbnail_from_eet(const char *group, const char *pkgname) {
    char buf[1024];
    char thumbnail_file[1024];
    Evas_Object *obj;
    Eet_File *ef;
    char **list;
    int num;
    int err;
    if (!pkgname) {
        debug("pkgname shall not be NULL!!\n");
        return NULL;
    }
    snprintf(thumbnail_file, sizeof(thumbnail_file), "%s/%s", THEME_PATH, THUMBNAIL_FILE);
    ef = eet_open(thumbnail_file, EET_FILE_MODE_READ);
    if (!ef) {
        debug("%s does not exists: use default with theme!!\n", thumbnail_file);
        return NULL;
    }
    if (group) {
        snprintf(buf, sizeof(buf), "/%s/%s", group, pkgname);
    } else {
        snprintf(buf, sizeof(buf), "*/%s", pkgname);
    }
    list = eet_list(ef, buf, &num);
    if (num == 0) {
        debug("Tag %s not found\n", buf);
        eet_close(ef);
        return NULL;
    }
    debug("buf == %s num=%d thumbnail_file:%s Tag:%s\n", buf, num, thumbnail_file, list[0]);
    obj = evas_object_image_add(ecore_evas_get(ee));
    evas_object_image_smooth_scale_set(obj, 0);
    evas_object_image_file_set(obj, thumbnail_file, list[0]);
    err = evas_object_image_load_error_get(obj);
    eet_close(ef);
    free(list);
    if (err == EVAS_LOAD_ERROR_NONE) {
        return obj;
    }
    error("Load Error!!\n");
    return NULL;
}

static struct Message_Alert *__message_alert_singalton__ = NULL;
static int __message_dismiss_cb (void *data) {
    struct Message_Alert *_ma = (struct Message_Alert *)data;
    if (!__message_alert_singalton__) {
        return 0;
    }
    if (__message_alert_singalton__ == _ma) {
        __message_alert_singalton__ = NULL;
    }
    edje_object_signal_callback_del(_ma->obj, "mouse,clicked,1", "button.ok", _ma->cb);
    evas_object_del(_ma->obj);
    remove_the_mask_on_screen();
    free(_ma);
    get_categories_page();
    scroll_page_back_to_root();
    return 0;
}

static void message_dismiss_cb (void *data,  Evas_Object *obj, const char *emission, const char *source) {
    struct Message_Alert *_ma = (struct Message_Alert *)data;
    int x, y, w, h;
    evas_object_geometry_get(_ma->obj, &x, &y, &w, &h);
    animator_move_object(_ma->obj, FINISH_TIME_MAGIC, x, y, x, -h);
    if (_ma->cb && _ma->cb != &message_dismiss_cb) {
        _ma->cb(data, obj, emission, source);
    }
    ecore_timer_add(FINISH_TIME_MAGIC*1.5, __message_dismiss_cb, _ma);
}

static int __make_sure_message_is_on_the_top(void *data) {
    struct Message_Alert *_ma = (struct Message_Alert *)__message_alert_singalton__;
    if (__message_alert_singalton__) {
        if (_ma->obj && evas_object_top_get(ecore_evas_get(ee)) != _ma->obj) {
            put_a_mask_on_screen();
            evas_object_raise(_ma->obj);
        }
        return 1;
    }
    return 0;
}

void message_alert(char *message, int message_level, CallbackFunc cb) {
    static struct Message_Alert * _ma;
    const int alert_height = 185;
    if (__message_alert_singalton__ ) {
        _ma = __message_alert_singalton__;
        if (_ma-> level > message_level) {
            return;
        }
        edje_object_signal_callback_del(_ma->obj, "mouse,clicked,1", "button.ok", _ma->cb);
    } else {
        put_a_mask_on_screen();
        _ma = (struct Message_Alert *)malloc (sizeof(struct Message_Alert));
        memset(_ma, 0 , sizeof(struct Message_Alert));
        __message_alert_singalton__ = _ma;
        _ma->obj = assassin_edje_object_get("assassin/message.alert");
        evas_object_resize(_ma->obj, WIDTH(), alert_height);
        debug("_ma->obj size is %d %d\n", WIDTH(), alert_height);
        evas_object_move(_ma->obj, 0 , -alert_height);
        animator_move_object (_ma->obj, FINISH_TIME_MAGIC, 0, -alert_height, 0, (HEIGHT() - alert_height) / 2);
    }

    _ma->level = message_level;
    _ma->cb = cb;
    edje_object_signal_callback_add(_ma->obj, "mouse,clicked,1", "button.ok", &message_dismiss_cb, _ma);
    if (message_level == NORMAL_MESSAGE) {
        edje_object_signal_emit(_ma->obj, "title,type,message", "title");
        edje_object_part_text_set(_ma->obj, "title", "MESSAGE:");
    } else if (message_level == NORMAL_WARNING) {
        edje_object_signal_emit(_ma->obj, "title,type,warning", "title");
        edje_object_part_text_set(_ma->obj, "title", "WARNING:");
    } else if (message_level == NORMAL_ERROR) {
        edje_object_signal_emit(_ma->obj, "title,type,error", "title");
        edje_object_part_text_set(_ma->obj, "title", "ERROR:");
    } else if (message_level == FATAL_ERROR) {
        edje_object_signal_emit(_ma->obj, "title,type,fatal", "title");
        edje_object_part_text_set(_ma->obj, "title", "FATAL:");
    } else {
        edje_object_signal_emit(_ma->obj, "title,type,message", "title");
        edje_object_part_text_set(_ma->obj, "title", "Hun?");
    }
    if (!message) {
        message = "Unknown message";
    }
    evas_object_show(_ma->obj);
    ecore_timer_add(0.05f, &__make_sure_message_is_on_the_top, NULL);
    edje_object_part_text_set(_ma->obj, "message", message);
}
